package concurrent.completationstage;

import concurrent.completationstage.realexample.CSBase;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import org.testng.annotations.Test;
import util.MathUtil;

/**
 * @see java.util.concurrent.CompletionStage 是接口，声明了方法
 * @see java.util.concurrent.CompletableFuture 是实现类，有更多方法
 *
 *
 *
 * allOf不能用Object表示返回类型，因为传入其中的future的返回类型是不确定的，所以无法用一个特定的类型表示返回类型是什么，
 * Object只能表示某一个类型，但是实际上可能是许多个类型
 *
 *
 *
 *
 * todo 完成一个不同返回类型的case
 *
 * 创建一个CompletionStage
 *
 *
 */
public class CompletableFuture1 {

    /**
     * 获取每一个allOf接收的future的结果
     */
    @Test
    private void t1() {
        List<String> list = Arrays.asList("a", "b", "c");

//      获得一个存储future的集合
        List<CompletableFuture<String>> futures = list.stream()
            .map(msg -> CompletableFuture.completedFuture(msg)
                .thenApply(CSBase::delayedUpperCase)
            )
            .collect(Collectors.toList());

//      获得所有all future的结果
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));

//      用回调函数得到allFutures的结果
        CompletableFuture<List<String>> resFuture = allFutures.thenApply(v ->
            futures.stream()
                .map(f -> f.getNow("undone"))
                .collect(Collectors.toList()));

//      对结果进行筛选
        var count = resFuture.thenApply(res -> res.stream()
            .filter(r -> r.length() > 0)
            .collect(Collectors.toList()));

        System.out.println(count.getNow(Collections.singletonList("none")));

    }


    @Test
    private void t2() {
//        如果是用接口声明对象，
        CompletionStage<?> stage = new CompletableFuture<>();


//      获得stage实际的实现类对象
        CompletableFuture<?> future = stage.toCompletableFuture();

//        然后可以得到结果，3种方式

        try {

            /**
             * InterruptedException if the current thread was interrupted while waiting
             * 如果处于等待状态的线程被中断
             * 需要等待
             */
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

//        也需要等待，
        future.join();

        future.getNow(null);

//      回调获取结果
        future.thenAccept(res -> {

        });

    }

    /**
     * 运行一个简单的异步stage
     */
    @Test
    private void t3() {

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            MathUtil.resolvePrime(9876543);
        });

        future.thenAccept(System.out::println);

    }

    @Test
    private void t4() {
        for (int i = 0; i < 20; i++) {
            MathUtil.resolvePrime(987654321098765432L);
        }

    }

    @Test
    private void t5() {
//        CompletableFuture<Object> f = new CompletableFuture<>();
//        f.thenCompose(res -> {
//
//
//            return 0;
//        });

    }

    /**
     * 为什么thenCompose的结果必须返回一个Stage，因为thenCompose处理之后需要是一个Stage，如果不返回Stage，那么
     */
    @Test
    private void t6() {
        String msg = "hello";
        CompletableFuture<String> future = CompletableFuture.completedFuture(msg);
        future.thenApply(res -> {
            System.out.println(res);
            return res;
        }).thenApply(res -> {
            // thenApply可以返回一个Stage
            return CompletableFuture.completedFuture(1);
        }).thenApply(res -> {
            // thenApply也可以不返回Stage
            return 1;
        });


        future.thenApply(res -> {
            System.out.println(res);
            return res;
        }).thenCompose(res -> {
            // no instance(s) of type variable(s) U exist so that Integer conforms to CompletionStage<U>
//            return 1;
            // thenCompose必须返回一个Stage，thenApply和thenCompose的区别在于参数，都是Function对象，但是返回值有区别
            return CompletableFuture.completedFuture(1);
        });

        var res = future
            .thenApply(r -> {
                var s = CompletableFuture.completedFuture(1)
                    .thenApply(r2 -> {
                        return r2;
                    })
                    .thenCompose(r3 -> {
                        return CompletableFuture.completedFuture("1");
                    });
                return s;
            })

            .thenCompose(r4 -> {
            return CompletableFuture.completedFuture(r4);
        })

            ;
    }
}
